package cryptohelp

import (
	"crypto/aes"
	"crypto/cipher"
	"crypto/rand"
	"encoding/base64"
	"errors"
	"fmt"
	"io"
)

type AesHelp struct {
}

// GenerateRandomKey 生成指定长度的随机密钥
// 参数size:16 bytes for AES-128，24 bytes for AES-192 ，32 bytes for AES-256
func (s *AesHelp) GenerateRandomKey(size int) ([]byte, error) {
	key := make([]byte, size)
	if _, err := io.ReadFull(rand.Reader, key); err != nil {
		return nil, err
	}
	return key, nil
}

func (s *AesHelp) GenerateRandomKeyStr(size int) (string, error) {
	key := make([]byte, size)
	if _, err := io.ReadFull(rand.Reader, key); err != nil {
		return "", err
	}
	return base64.StdEncoding.EncodeToString(key), nil
}

// EncryptByGCM 使用 AES-GCM 加密数据
func (s *AesHelp) EncryptByGCM(key, plaintext []byte) ([]byte, error) {
	block, err := aes.NewCipher(key)
	if err != nil {
		return nil, fmt.Errorf("创建加密块失败: %v", err)
	}

	gcm, err := cipher.NewGCM(block)
	if err != nil {
		return nil, fmt.Errorf("创建GCM模式失败: %v", err)
	}

	nonce := make([]byte, gcm.NonceSize())
	if _, err = io.ReadFull(rand.Reader, nonce); err != nil {
		return nil, fmt.Errorf("生成nonce失败: %v", err)
	}

	ciphertext := gcm.Seal(nonce, nonce, plaintext, nil)
	return ciphertext, nil
}

// DecryptByGCM 使用 AES-GCM 解密数据
func (s *AesHelp) DecryptByGCM(key, ciphertext []byte) ([]byte, error) {
	block, err := aes.NewCipher(key)
	if err != nil {
		return nil, fmt.Errorf("创建加密块失败: %v", err)
	}

	gcm, err := cipher.NewGCM(block)
	if err != nil {
		return nil, fmt.Errorf("创建GCM模式失败: %v", err)
	}

	nonceSize := gcm.NonceSize()
	if len(ciphertext) < nonceSize {
		return nil, errors.New("密文长度无效")
	}

	nonce, ciphertext := ciphertext[:nonceSize], ciphertext[nonceSize:]
	plaintext, err := gcm.Open(nil, nonce, ciphertext, nil)
	if err != nil {
		return nil, fmt.Errorf("解密失败: %v", err)
	}

	return plaintext, nil
}

func (s *AesHelp) EncryptStrByGCM(key, plaintext string) (string, error) {
	bc := Base64Helper{}
	re, err := s.EncryptByGCM(bc.DecodeString(key), bc.DecodeString(plaintext))
	if err != nil {
		return "", err
	}
	return base64.StdEncoding.EncodeToString(re), nil
}

func (s *AesHelp) DecryptStrByGCM(key, plaintext string) (string, error) {
	bc := Base64Helper{}
	re, err := s.DecryptByGCM(bc.DecodeString(key), bc.DecodeString(plaintext))
	if err != nil {
		return "", err
	}
	return string(re), nil
}
